//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#ifndef __SAGITTARIUS__UNICODE_STRING__H_
#define __SAGITTARIUS__UNICODE_STRING__H_

//== INCLUDES ========================================================================

#include "sagittarius/core/SagittariusCore.hpp"

#include "sagittarius/unicode/UnicodeChar.hpp"
#include "sagittarius/patterns/Array.hpp"
#include <sstream>
#include "boost/scoped_array.hpp"

namespace Sagittarius
{
    
    class UnicodeString
    {
        protected:
            typedef UnicodeChar CharType;
            typedef Array<CharType> BufferType;
        public:
            typedef CharType charT;
            
            //empty constructor
            UnicodeString()
            {
            }
            
            //copy constructor
            UnicodeString(const UnicodeString& other)
                : _buffer(other._buffer)
                {}
            
            //from buffer
            explicit UnicodeString(const BufferType& buffer)
                : _buffer(buffer) {}
            
            //from C-style string
            UnicodeString(const char* str)
            //UnicodeString(const char* str)
            {
                From_UTF_8_String(str, get_end_of_buffer(str));
            }
            
            //from std::string string
            explicit UnicodeString(const std::string& str)
            {
                From_UTF_8_String(str.c_str(), str.c_str()+str.size());
            }
            
            //construct from c-style UTF-8 string
            void From_UTF_8_String(const char* c_str, const char* end_of_buffer)
            {
                _buffer.clear();
                const char* p(c_str);
                int i=0;
                while(*p != '\0' && p < end_of_buffer)
                {
                    _buffer.push_back(CharType());
                    try {
                        p = _buffer.back().From_UTF_8_String(p, end_of_buffer);
                    } catch(Exception e) {
                        std::stringstream s;
                        s << "Error converting character " << i+1 << ", byte " << p-c_str << ": " << e.getDescription();
                        SAGITTARIUS_THROW(SanityCheckException, s.rdbuf()->str(), "UnicodeChar::From_UTF_8_String");
                    }
                    ++i;
                }
            }
            
            //get the total number of bytes in the buffer
            int n_bytes() const
            {
                int result=0;
                for(BufferType::const_iterator iter(_buffer.begin()); iter!=_buffer.end(); ++iter)
                {
                    result += iter->n_bytes();
                }
            }
            
            //convert to c-style string
            char* ToC_String()
            {
                _c_str.reset(new char[n_bytes()]);
                char* cursor(_c_str.get());
                //end of buffer
                char* eob(_c_str.get()+n_bytes());
                
                for(BufferType::const_iterator iter(_buffer.begin()); iter!=_buffer.end(); ++iter)
                {
                    cursor = iter->ToC_String(cursor, eob);
                    #if SAGITTARIUS_DEBUG_LEVEL >= 1
                        SAGITTARIUS_ASSERT(cursor < eob);
                    #endif
                }
                
                return _c_str.get();
            }
            
            //convert to c-style string
            std::string ToStdString() const
            {
                std::string result;
                if(_buffer.size() < 1)
                    return result;
                
                for(BufferType::const_iterator iter(_buffer.begin()); iter!=_buffer.end(); ++iter)
                {
                    result += iter->ToStdString();
                }
                
                return result;
            }
            
            //convert to codepoint string
            std::string ToCodepointString() const
            {
                //std::cout << "UnicodeString::ToCodepointString started\n";
                std::string result;
                
                //std::cout << "UnicodeString::ToCodepointString sizeof(CharType): " << sizeof(CharType) << "\n";
                //std::cout << "UnicodeString::ToCodepointString _buffer.size() = " << _buffer.size() << ", _buffer.begin() == " << _buffer.begin() << ", _buffer.end() = " << _buffer.end() << "\n";
                for(BufferType::const_iterator iter(_buffer.begin()); iter!=_buffer.end(); ++iter)
                {
                    //std::cout << "shit1\n";
                    //std::cout << "UnicodeString::ToCodepointString iteration: iter = " << iter << "\n";
                    result += iter->ToCodepointString();
                    //std::cout << "UnicodeString::ToCodepointString string = " << result << "\n";
                    //std::cout << "shit2\n";
                }
                
                return result;
            }
            
            //append
            void append(const CharType& chr)
            {
                _buffer.push_back(chr);
            }
            
            //get pointer to data
            const CharType* get() const
            {
                return _buffer.get();
            }
            
            //reserve
            void reserve(const unsigned int size)
            {
                _buffer.reserve(size);
            }
            
            //clear
            void clear()
            {
                _buffer.clear();
            }
            
            //size
            int size() const { return _buffer.size(); }
            
            //resize_empty
            void resize_empty(const int size)
            {
                _buffer.resize_empty(size);
            }
            
            //comparison
            bool operator==(const UnicodeString& other) const
            {
                //std::cout << "UnicodeString::operator== started, _buffer == other._buffer = " << (bool)(_buffer == other._buffer) << "\n";
                return _buffer == other._buffer;
            }
            
            bool operator!=(const UnicodeString& other) const
            {
                return !this->operator==(other);
            }
            
            //addition
            const UnicodeString operator+ (const UnicodeString& other) const
            {
                BufferType buffer(_buffer);
                for(BufferType::const_iterator iter(other._buffer.begin()); iter!=other._buffer.end(); ++iter)
                    buffer.push_back(*iter);
                return UnicodeString(buffer);
            }
            const UnicodeString operator+ (const char* other) const
            {
                UnicodeString other_str(other);
                return this->operator+ (other_str);
            }
            
            //operator[]
            CharType& operator[] (const int i) { return _buffer[i]; }
            const CharType& operator[] (const int i) const { return _buffer[i]; }
            
            //assignment
            UnicodeString& operator=(const UnicodeString& other)
            {
                _buffer = other._buffer;
                return *this;
            }
            //from std::string
            /*UnicodeString& operator=(const std::string& str)
            {
                for(std::string::const_iterator iter(str.begin()); iter!=str.end(); ++iter)
                {
                    CharType uni_char;
                    uni_char.From_UTF_8_String(&(*iter), &(*str.end())); //NOTE: this requires std::string buffer contiguity
                    _buffer.push_back(uni_char);
                }
                return *this;
            }*/
            
            //iterators
            typedef BufferType::iterator iterator;
            typedef BufferType::const_iterator const_iterator;
            
            //begin
            iterator        begin()         { return _buffer.begin(); }
            const_iterator  begin() const   { return _buffer.begin(); }
            
            //end
            iterator        end()         { return _buffer.end(); }
            const_iterator  end() const   { return _buffer.end(); }
            
        protected:
            const char* get_end_of_buffer(const char* str)
            {
                //finds the null character
                const char* ch=str;
                while(*ch != '\0')
                    ++ch;
                return ch;
            }
            
            //buffer
            BufferType _buffer;
            boost::scoped_array<char> _c_str;
    };
    
    //stream I/O
    std::ostream& operator<<(std::ostream& os, const UnicodeString& str);
    
} //namespace Sagittarius

#endif
